#pragma once

#include <QList>

#include "../widgets.h"
#include "basechart_axis.h"
#include "basechart_datacontainer.h"
#include "basechart_layer.h"
#include "basechart_legend.h"
#include "basechart_painter.h"
#include "basechart_plot.h"
#include "basechart_plottable.h"
#include "basechart_selection.h"

/* including file 'src/plottables/plottable-bars.h' */
/* modified 2021-03-29T02:30:44, size 8955          */
class BaseChartBars;
class S_WIDGETS_EXPORT BaseChartBarsGroup : public QObject {
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(SpacingType spacingType READ spacingType WRITE setSpacingType)
  Q_PROPERTY(double spacing READ spacing WRITE setSpacing)
  /// \endcond
 public:
  /*!
    Defines the ways the spacing between bars in the group can be specified.
    Thus it defines what the number passed to \ref setSpacing actually means.

    \see setSpacingType, setSpacing
  */
  enum SpacingType {
    stAbsolute  ///< Bar spacing is in absolute pixels
    ,
    stAxisRectRatio  ///< Bar spacing is given by a fraction of the axis rect
                     ///< size
    ,
    stPlotCoords  ///< Bar spacing is in key coordinates and thus scales with
                  ///< the key axis range
  };
  Q_ENUMS(SpacingType)

  explicit BaseChartBarsGroup(BaseChartCustomPlot* parentPlot);
  virtual ~BaseChartBarsGroup();

  // getters:
  SpacingType spacingType() const { return mSpacingType; }
  double spacing() const { return mSpacing; }

  // setters:
  void setSpacingType(SpacingType spacingType);
  void setSpacing(double spacing);

  // non-virtual methods:
  QList<BaseChartBars*> bars() const { return mBars; }
  BaseChartBars* bars(int index) const;
  int size() const { return mBars.size(); }
  bool isEmpty() const { return mBars.isEmpty(); }
  void clear();
  bool contains(BaseChartBars* bars) const { return mBars.contains(bars); }
  void append(BaseChartBars* bars);
  void insert(int i, BaseChartBars* bars);
  void remove(BaseChartBars* bars);

 protected:
  // non-property members:
  BaseChartCustomPlot* mParentPlot;
  SpacingType mSpacingType;
  double mSpacing;
  QList<BaseChartBars*> mBars;

  // non-virtual methods:
  void registerBars(BaseChartBars* bars);
  void unregisterBars(BaseChartBars* bars);

  // virtual methods:
  double keyPixelOffset(const BaseChartBars* bars, double keyCoord);
  double getPixelSpacing(const BaseChartBars* bars, double keyCoord);

 private:
  Q_DISABLE_COPY(BaseChartBarsGroup)

  friend class BaseChartBars;
};

Q_DECLARE_METATYPE(BaseChartBarsGroup::SpacingType)

class S_WIDGETS_EXPORT BaseChartBarsData {
 public:
  BaseChartBarsData();
  BaseChartBarsData(double key, double value);

  inline double sortKey() const { return key; }
  inline static BaseChartBarsData fromSortKey(double sortKey) {
    return BaseChartBarsData(sortKey, 0);
  }
  inline static bool sortKeyIsMainKey() { return true; }

  inline double mainKey() const { return key; }
  inline double mainValue() const { return value; }

  inline BaseChartRange valueRange() const {
    return BaseChartRange(value, value);
  }  // note that bar base value isn't held in each BaseChartBarsData and thus
     // can't/shouldn't be returned here

  double key, value;
};
Q_DECLARE_TYPEINFO(BaseChartBarsData, Q_PRIMITIVE_TYPE);

/*! \typedef BaseChartBarsDataContainer

  Container for storing \ref BaseChartBarsData points. The data is stored sorted
  by \a key.

  This template instantiation is the container in which BaseChartBars holds its
  data. For details about the generic container, see the documentation of the
  class template \ref BaseChartDataContainer.

  \see BaseChartBarsData, BaseChartBars::setData
*/
typedef BaseChartDataContainer<BaseChartBarsData> BaseChartBarsDataContainer;

class S_WIDGETS_EXPORT BaseChartBars
    : public BaseChartAbstractPlottable1D<BaseChartBarsData> {
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(double width READ width WRITE setWidth)
  Q_PROPERTY(WidthType widthType READ widthType WRITE setWidthType)
  Q_PROPERTY(BaseChartBarsGroup* barsGroup READ barsGroup WRITE setBarsGroup)
  Q_PROPERTY(double baseValue READ baseValue WRITE setBaseValue)
  Q_PROPERTY(double stackingGap READ stackingGap WRITE setStackingGap)
  Q_PROPERTY(BaseChartBars* barBelow READ barBelow)
  Q_PROPERTY(BaseChartBars* barAbove READ barAbove)
  /// \endcond
 public:
  /*!
    Defines the ways the width of the bar can be specified. Thus it defines what
    the number passed to \ref setWidth actually means.

    \see setWidthType, setWidth
  */
  enum WidthType {
    wtAbsolute  ///< Bar width is in absolute pixels
    ,
    wtAxisRectRatio  ///< Bar width is given by a fraction of the axis rect size
    ,
    wtPlotCoords  ///< Bar width is in key coordinates and thus scales with the
                  ///< key axis range
  };
  Q_ENUMS(WidthType)

  explicit BaseChartBars(BaseChartAxis* keyAxis, BaseChartAxis* valueAxis);
  virtual ~BaseChartBars() Q_DECL_OVERRIDE;

  // getters:
  double width() const { return mWidth; }
  WidthType widthType() const { return mWidthType; }
  BaseChartBarsGroup* barsGroup() const { return mBarsGroup; }
  double baseValue() const { return mBaseValue; }
  double stackingGap() const { return mStackingGap; }
  BaseChartBars* barBelow() const { return mBarBelow.data(); }
  BaseChartBars* barAbove() const { return mBarAbove.data(); }
  QSharedPointer<BaseChartBarsDataContainer> data() const {
    return mDataContainer;
  }
  QRectF getBarRect(double key, double value) const;

  // setters:
  void setData(QSharedPointer<BaseChartBarsDataContainer> data);
  void setData(const QVector<double>& keys, const QVector<double>& values,
               bool alreadySorted = false);
  void setWidth(double width);
  void setWidthType(WidthType widthType);
  void setBarsGroup(BaseChartBarsGroup* barsGroup);
  void setBaseValue(double baseValue);
  void setStackingGap(double pixels);
  void setLight(int a);
  void setLightRect(QRectF rect);

  // non-property methods:
  void addData(const QVector<double>& keys, const QVector<double>& values,
               bool alreadySorted = false);
  void addData(double key, double value);
  void moveBelow(BaseChartBars* bars);
  void moveAbove(BaseChartBars* bars);

  // reimplemented virtual methods:
  virtual BaseChartDataSelection selectTestRect(
      const QRectF& rect, bool onlySelectable) const Q_DECL_OVERRIDE;
  virtual double selectTest(const QPointF& pos, bool onlySelectable,
                            QVariant* details = nullptr) const Q_DECL_OVERRIDE;
  virtual BaseChartRange getKeyRange(
      bool& foundRange, SignDomain inSignDomain = sdBoth) const Q_DECL_OVERRIDE;
  virtual BaseChartRange getValueRange(
      bool& foundRange, SignDomain inSignDomain = sdBoth,
      const BaseChartRange& inKeyRange = BaseChartRange()) const
      Q_DECL_OVERRIDE;
  virtual QPointF dataPixelPosition(int index) const Q_DECL_OVERRIDE;

 protected:
  // property members:
  int mLight;
  QRectF mLightRect;

  double mWidth;
  WidthType mWidthType;
  BaseChartBarsGroup* mBarsGroup;
  double mBaseValue;
  double mStackingGap;
  QPointer<BaseChartBars> mBarBelow, mBarAbove;

  // reimplemented virtual methods:
  virtual void draw(BaseChartPainter* painter) Q_DECL_OVERRIDE;
  virtual void drawLegendIcon(BaseChartPainter* painter,
                              const QRectF& rect) const Q_DECL_OVERRIDE;

  // non-virtual methods:
  void getVisibleDataBounds(
      BaseChartBarsDataContainer::const_iterator& begin,
      BaseChartBarsDataContainer::const_iterator& end) const;
  void getPixelWidth(double key, double& lower, double& upper) const;
  double getStackedBaseValue(double key, bool positive) const;
  static void connectBars(BaseChartBars* lower, BaseChartBars* upper);

  friend class BaseChartCustomPlot;
  friend class BaseChartLegend;
  friend class BaseChartBarsGroup;
};
Q_DECLARE_METATYPE(BaseChartBars::WidthType)

/* end of 'src/plottables/plottable-bars.h' */